﻿#include "FITKGraphObjectSpringPoint2PointPreview.h"

// VTK
#include <vtkPoints.h>
#include <vtkUnstructuredGrid.h>
#include <vtkUnsignedCharArray.h>
#include <vtkCellArray.h>
#include <vtkPolyData.h>
#include <vtkProperty.h>
#include <vtkCellData.h>

// For VTK 7
//@{
#include <vtkCellType.h>
//@}

// Graph
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"
#include "FITKGraphCommons.h"

// Render
#include "FITK_Component/FITKRenderWindowVTK/FITKGraphRender.h"

// Global data
#include "FITK_Kernel/FITKCore/FITKDataRepo.h"

// Data manager
#include "FITKSetSurfPolyProvider.h"

// Data
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaSpringDashpotPoint2Point.h"

namespace Exchange
{
    FITKGraphObjectSpringPoint2PointPreview::FITKGraphObjectSpringPoint2PointPreview(AbaqusData::FITKDataCase* caseData, Interface::FITKAbaSpringDashpotPoint2Point* springData)
        : FITKGraphObjectMarkBase(caseData, springData)
    {
        if (!springData)
        {
            return;
        }

        // Initialize.
        init();

        // Generate the spring's node poly data for glyph.
        bool flag = generatePolyData(springData);
        if (!flag)
        {
            return;
        }

        // Set the layer need to be rendered.
        setRenderLayer(1, 1);

        // Generate actor.
        generateGraph();

        // Update the visibility.
        updateVisibility();
        
        // Save if the bounds are fixed or dynamic.
        m_hasFixedBounds = false;
    }

    FITKGraphObjectSpringPoint2PointPreview::~FITKGraphObjectSpringPoint2PointPreview()
    {
        // Delete grid data.
        //@{
        deleteVtkObj(m_pointsPt);
        deleteVtkObj(m_ugridPt);
        deleteVtkObj(m_pointsLine);
        deleteVtkObj(m_colorArrayPt);
        deleteVtkObj(m_ugridLine);
        //@}

        // Delete the grid data for advance-highlighting.
        deleteVtkObj(m_pointsAdv);
        deleteVtkObj(m_ugridAdv);
    }

    void FITKGraphObjectSpringPoint2PointPreview::setVisible(bool visibility)
    {
        // Show or hide line and point actor.
        if (m_fActorPt)
        {
            m_fActorPt->SetVisibility(visibility);
        }

        if (m_fActorLine)
        {
            m_fActorLine->SetVisibility(visibility);
        }
    }

    void FITKGraphObjectSpringPoint2PointPreview::init()
    {
        // Create grid data for points.
        //@{
        m_ugridPt = vtkUnstructuredGrid::New();
        m_pointsPt = vtkPoints::New();
        m_ugridPt->SetPoints(m_pointsPt);
        m_colorArrayPt = vtkUnsignedCharArray::New();
        m_colorArrayPt->SetNumberOfComponents(3);
        m_colorArrayPt->SetName("_color_");
        m_ugridPt->GetCellData()->AddArray(m_colorArrayPt);
        //@}

        // Create grid data for lines( dashed ).
        //@{
        m_ugridLine = vtkUnstructuredGrid::New();
        m_pointsLine = vtkPoints::New();
        m_ugridLine->SetPoints(m_pointsLine);
        //@}

        // Initialize grid data for advanced-highlighting.
        //@{
        m_ugridAdv = vtkUnstructuredGrid::New();
        m_pointsAdv = vtkPoints::New();
        m_ugridAdv->SetPoints(m_pointsAdv);
        //@}
    }

    void FITKGraphObjectSpringPoint2PointPreview::initActors()
    {
        // Get the spring highlight color for the color of line.
        double color3[3] = { 0 ,0, 0 };
        FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_springHighlightColor, color3);

        // Create the actor for points.
        m_fActorPt = FITKGraphActor::New();
        m_fActorPt->setGraphObject(this);
        m_fActorPt->SetPickable(false);
        m_fActorPt->SetVisibility(true);
        m_fActorPt->GetProperty()->SetRepresentation(0);
        m_fActorPt->GetProperty()->SetPointSize(m_vertexSize);
        setLightPropertiesForLine(m_fActorPt);
        addActor(m_fActorPt);

        // Initialize points scalar property.
        m_fActorPt->setScalarVisibility(true);
        m_fActorPt->setScalarModeToUseCellFieldData();
        m_fActorPt->selectScalarArray(m_colorArrayPt->GetName());

        // Create the actor for lines.
        m_fActorLine = FITKGraphActor::New();
        m_fActorLine->setGraphObject(this);
        m_fActorLine->SetPickable(false);
        m_fActorLine->SetVisibility(true);
        m_fActorLine->setScalarVisibility(false);
        m_fActorLine->GetProperty()->SetRepresentation(1);
        m_fActorLine->GetProperty()->SetColor(color3);
        m_fActorLine->GetProperty()->SetLineWidth(2);
        setLightPropertiesForLine(m_fActorLine);
        addActor(m_fActorLine);

        // Create the actor for advanced-highlighting.
        m_fActorAdv = FITKGraphActor::New();
        m_fActorAdv->setGraphObject(this);
        m_fActorAdv->SetPickable(false);
        m_fActorAdv->SetVisibility(false);
        m_fActorAdv->setScalarVisibility(false);
        m_fActorAdv->GetProperty()->SetRepresentation(1);
        m_fActorAdv->GetProperty()->SetColor(color3);
        m_fActorAdv->GetProperty()->SetLineWidth(4);
        setLightPropertiesForLine(m_fActorAdv);
        addActor(m_fActorAdv);
    }

    void FITKGraphObjectSpringPoint2PointPreview::clearData()
    {
        // Clear all data.
        resetVtkObj(m_pointsPt);
        resetVtkObj(m_ugridPt);

        resetVtkObj(m_pointsLine);
        resetVtkObj(m_colorArrayPt);
        resetVtkObj(m_ugridLine);

        // Reset the advanced-highlight grid data.
        clearAdvanceData();

        // Clear the model infomation.
        clearModelsInfo();
    }

    void FITKGraphObjectSpringPoint2PointPreview::clearAdvanceData()
    {
        resetVtkObj(m_pointsAdv);
        resetVtkObj(m_ugridAdv);
    }

    void FITKGraphObjectSpringPoint2PointPreview::highlight(QColor color)
    {
        // This graph object cannot be highlighted.
        Q_UNUSED(color);
        return;
    }

    void FITKGraphObjectSpringPoint2PointPreview::disHighlight()
    {
        // This graph object cannot be highlighted.
        return;
    }

    void FITKGraphObjectSpringPoint2PointPreview::advanceHighlight(QVariant details, QColor color)
    {
        // Reset data.
        clearAdvanceData();

        Interface::FITKAbaSpringDashpotPoint2Point* springData = dynamic_cast<Interface::FITKAbaSpringDashpotPoint2Point*>(_dataObj);
        if (!springData)
        {
            return;
        }

        // Check the given indice of spring pair.
        if (!details.isValid())
        {
            return;
        }

        QVariantList vList = details.toList();
        if (vList.count() == 0)
        {
            return;
        }

        for (const QVariant & var : vList)
        {
            // Get the line by the given data id.
            int lineId = var.toInt();
            Interface::FITKAbaAssEle3D2* line = springData->getDataByID(lineId);
            if (!line)
            {
                continue;
            }

            // Generate data.
            //@{
            // Get the positions.
            double coord_1[3]{ 0., 0., 0. };
            double coord_2[3]{ 0., 0., 0. };
            line->getCoor1(coord_1);
            line->getCoor2(coord_2);

            // Get the model( instance ) id of the node.
            int modelId_1 = -1, modelId_2 = -1;

            // Get the node id of the model.
            int nodeId_1 = -1, nodeId_2 = -1;

            line->getNode1(modelId_1, nodeId_1);
            line->getNode2(modelId_2, nodeId_2);

            // Check if node 1 is valid.
            bool node1Valid = false;
            if (modelId_1 > 0 && nodeId_1 > 0)
            {
                node1Valid = true;
            }

            // Check if node 2 is valid.
            bool node2Valid = false;
            if (modelId_2 > 0 && nodeId_2 > 0)
            {
                node2Valid = true;
            }

            if (!node1Valid || !node2Valid)
            {
                continue;
            }

            // Generate poly data for glyph and line.
            int ptLine_1 = m_pointsAdv->InsertNextPoint(coord_1);
            int ptLine_2 = m_pointsAdv->InsertNextPoint(coord_2);

            vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
            cell->InsertNextId(ptLine_1);
            cell->InsertNextId(ptLine_2);
            m_ugridAdv->InsertNextCell(VTKCellType::VTK_LINE, cell);
            //@}
        }

        // Get highlight color.
        double color3[3] = { 0, 0, 0 };
        if (color.isValid())
        {
            FITKGraphCommons::QColorToDouble3(color, color3);
        }
        else
        {
            FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_springHighlightColor, color3);
        }

        // Show the advance actor.
        //@{
        if (m_fActorAdv)
        {
            m_fActorAdv->GetProperty()->SetColor(color3);
            m_fActorAdv->SetVisibility(true);
        }
        
        this->Superclass::advanceHighlight();
    }

    void FITKGraphObjectSpringPoint2PointPreview::disAdvanceHighlight()
    {
        // Reset data.
        clearAdvanceData();

        // Hide the advaced-highlight actor.
        if (m_fActorAdv)
        {
            m_fActorAdv->SetVisibility(false);
        }

        this->Superclass::disAdvanceHighlight();
    }

    void FITKGraphObjectSpringPoint2PointPreview::update(bool forceUpdate)
    {
        if (!forceUpdate)
        {
            return;
        }

        Interface::FITKAbaSpringDashpotPoint2Point* springData = dynamic_cast<Interface::FITKAbaSpringDashpotPoint2Point*>(_dataObj);
        if (!springData)
        {
            return;
        }

        // Clear data first.
        clearData();

        // Force update.( Re-generate grid data. )
        generatePolyData(springData);

        // Update the whole visibility at last.
        updateVisibility();
    }

    void FITKGraphObjectSpringPoint2PointPreview::updateVisibility()
    {
        // Update the full actor only.
        FITKGraphObject3D::updateVisibility();
    }

    void FITKGraphObjectSpringPoint2PointPreview::generateGraph()
    {
        if (!m_pointsPt || !m_ugridPt || !m_pointsLine || !m_colorArrayPt || !m_ugridLine)
        {
            return;
        }

        // Initialize the actors.
        initActors();

        // Set the grid data.
        m_fActorPt->setInputDataObject(m_ugridPt);
        m_fActorLine->setInputDataObject(m_ugridLine);
        m_fActorAdv->setInputDataObject(m_ugridAdv);
    }

    bool FITKGraphObjectSpringPoint2PointPreview::generatePolyData(Interface::FITKAbaSpringDashpotPoint2Point* springData)
    {
        // Get the spring pair count.
        int nPair = springData->getDataCount();
        for (int i = 0; i < nPair; i++)
        {
            // Get the spring line.
            Interface::FITKAbaAssEle3D2* line = springData->getDataByIndex(i);
            if (!line)
            {
                continue;
            }

            // Get the positions.
            double coord_1[3]{ 0., 0., 0. };
            double coord_2[3]{ 0., 0., 0. };
            line->getCoor1(coord_1);
            line->getCoor2(coord_2);

            // Get the model( instance ) id of the node.
            int modelId_1 = -1, modelId_2 = -1;

            // Get the node id of the model.
            int nodeId_1 = -1, nodeId_2 = -1;

            line->getNode1(modelId_1, nodeId_1);
            line->getNode2(modelId_2, nodeId_2);

            // Check if node 1 is valid.
            bool node1Valid = false;
            if (modelId_1 > 0 && nodeId_1 > 0)
            {
                node1Valid = true;

                // Generate poly data for points.
                int ptId = m_pointsPt->InsertNextPoint(coord_1);

                vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
                cell->InsertNextId(ptId);
                m_ugridPt->InsertNextCell(VTKCellType::VTK_VERTEX, cell);

                // Add color to color array.
                m_colorArrayPt->InsertNextTuple3(m_startColor.red(), m_startColor.green(), m_startColor.blue());
            }

            // Check if node 2 is valid.
            bool node2Valid = false;
            if (modelId_2 > 0 && nodeId_2 > 0)
            {
                node2Valid = true;

                // Generate poly data for points.
                int ptId = m_pointsPt->InsertNextPoint(coord_2);

                vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
                cell->InsertNextId(ptId);
                m_ugridPt->InsertNextCell(VTKCellType::VTK_VERTEX, cell);

                // Add color to color array.
                m_colorArrayPt->InsertNextTuple3(m_endColor.red(), m_endColor.green(), m_endColor.blue());
            }

            if (node1Valid && node2Valid)
            {
                // Generate poly data for lines.
                int ptLine_1 = m_pointsLine->InsertNextPoint(coord_1);
                int ptLine_2 = m_pointsLine->InsertNextPoint(coord_2);

                vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
                cell->InsertNextId(ptLine_1);
                cell->InsertNextId(ptLine_2);
                m_ugridLine->InsertNextCell(VTKCellType::VTK_LINE, cell);
            }
        }

        return true;
    }
}   // namespace Exchange